#include "HashTable.h"

Node* HT_Init(void)
{
    Node* Dictionary;
    
    int i=0;
    
    Dictionary = (Node*)calloc(HASHSIZE,sizeof(Node));
    
    for(;i < HASHSIZE;i++)
    {
        if(i < 256)
            HT_Add(Dictionary, (uint8_t)i, 0, i);
        
        Dictionary[i].next = NULL;
        
    }
    
    return Dictionary;
}

//Funzione per l'inserimento di un dato nella tabella Hash
void HT_Add(Node *hashtable, uint8_t symbol, uint16_t father, uint16_t dictionaryindex) 
{
    uint16_t index = 0;
    
    //Determinazione dell'hash
    index = HT_Hash(symbol,father);
    
    if(hashtable[index].son != 0) 
    {
        //Risulta collissione -> la risolvo
        HT_ResolveCollision(hashtable, index, symbol, father, dictionaryindex);
    } 
    else 
    {
        hashtable[index].symbol = symbol;
        hashtable[index].father = father;
        hashtable[index].son = dictionaryindex;
        hashtable[index].next = NULL;
    }
}

//Funzione per la creazione dell'Hash
uint16_t HT_Hash(uint8_t symbol, uint16_t father) 
{
    uint16_t index = (13 * symbol) + (father<<(symbol>>3));
    
    index = index % HASHSIZE;
    
    return index;
}

uint16_t HT_Lookup(Node *hashtable, uint8_t symbol, uint16_t father)
{
    uint16_t index = HT_Hash(symbol, father);
    
    if(hashtable[index].son != 0)
    {
        if(hashtable[index].symbol == symbol && hashtable[index].father == father)
            return hashtable[index].son;
        else
        {
            if(hashtable[index].next == NULL)
                return 0;
            else 
            {
                Node* tmp = hashtable[index].next;
                
                while(tmp != NULL)
                {
                    if(tmp->symbol == symbol && tmp->father == father)
                        return tmp->son;
                    else tmp = tmp->next;
                }
                return 0;
            }
        }
    }
    return 0;
}

//Funzione che risolve le collissioni per elementi già presenti ad una certa posizione
//della tabella Hash
void HT_ResolveCollision(Node *hashtable, int loc, uint8_t symbol, uint16_t father, uint16_t dictionaryindex) 
{
    Node *tmp;
    tmp = &hashtable[loc];
    
    while(tmp->next != NULL)
        tmp = tmp->next;
    
    tmp->next = (Node*)calloc(1,sizeof(Node));
    tmp = tmp->next;
    tmp->symbol = symbol;
    tmp->father = father;
    tmp->son = dictionaryindex;
    tmp->next = NULL;
}

//Stampa a video la tabella Hash
void HT_Print(Node *hashtable) 
{
    int i = 0;
    Node *target;
    
    for(i = 0; i < HASHSIZE; i++) 
    {
        if(hashtable[i].son != 0 /*&& hashtable[i].son > 255*/) 
        {
            target = &hashtable[i];
            while(target) 
                printf("KEY -> %d\tFather : %d\tSymbol : %s \t Index : %d\n", i, target->father, &target->symbol, target->son), target = target->next;
        } 
    } 
}


//Funzione per cancellare una entry dalla tabella Hash
void HT_Delete(Node *Dictionary) 
{
    Node *nodetolink;
    Node *nodetofree;
    int index = 0;
    
    for(;index < HASHSIZE; index++)
    {
        
        //Nessun elemento all'indice selezionata
        if(Dictionary[index].son == 0)
            continue;
        
        //printf("Freeing hash entry %d\n",index);
        
        //C'è solo un elemento all'indice selezionato
        if(Dictionary[index].next == NULL) 
            continue;
        
        else 
        {
            //Ho una catena all'indice selezionato
            nodetofree = nodetolink = Dictionary + index;
            
            while(nodetofree->next != NULL) 
            {
                nodetofree = nodetofree->next;
                
                free(nodetolink->next);
                
                nodetolink = nodetofree;
                
            } 
        } 
    } 
    return;
}
